import { _decorator, Component, Enum, EventHandler, UITransform, Node, NodeEventType, v2, EventTouch, v3 } from 'cc';
import { PoolFactory } from './PoolFactory';
import { List } from './List';
import { ListRender } from './ListRender';
import { MathUtil } from '../../Util/MathUtil';
import { CallBackHandler } from '../Model/PoolObj/CallBackHandler';

const { ccclass, property, menu } = _decorator;

@ccclass('ListRoll')
@menu('myComponent/ListRoll')
export class ListRoll extends Component {
  /**对象池 */
  @property({ type: PoolFactory, tooltip: '对象池' })
  public pool: PoolFactory = null;

  /**列表项名 */
  @property({
    tooltip: '列表项名',
  })
  public itemName: string = '';

  /**列表项之间X间隔 */
  @property({
    tooltip: '列表项X间隔',
  })
  public spaceX: number = 0;

  /**列表项之间Y间隔 */
  @property({
    tooltip: '列表项Y间隔',
  })
  public spaceY: number = 0;

  @property({
    type: Enum(List.ListType),
    tooltip: '滚动方向',
  })
  protected rollDirection: number = List.ListType.Vertical;

  @property({
    tooltip: '速度',
  })
  speed: number = 1;

  @property
  canRoll: boolean = false;

  @property({
    type: EventHandler,
    tooltip: '点击事件',
  })
  clickEvent: EventHandler = new EventHandler();

  /** 容器 */
  protected contentForm: UITransform;

  /**列表项数据*/
  protected itemDataList: Array<unknown> = [];
  /**应创建的实例数量 */
  protected spawnCount: number = 0;
  /**存放列表项实例的数组 */
  protected itemList: Array<Node> = [];
  /**item的高度 */
  protected itemHeight: number = 0;
  /**item的宽度 */
  protected itemWidth: number = 0;
  /**item由于锚点不同的偏移 */
  protected moveX: number = 0;
  /**item由于锚点不同的偏移 */
  protected moveY: number = 0;
  /** 移动方向上item的单位量 */
  protected itemSize: number = 0;
  /** 更新节点 */
  private updateFunc: (begin: number, count: number) => void = null;
  /** 移动节点 */
  private moveFunc: (long: number) => void = null;

  //======================= 计算参数 ==========================
  /** 现在的第一条数据下标 */
  // protected nowContentPos: number = 0;
  /**网格行数 */
  protected gridRow: number = 0;
  /**网格列数 */
  protected gridCol: number = 0;
  /** 固定的行/列 */
  protected grid: number = 0;
  /**可以看到的item数 */
  protected seeNum: number = 0;
  /**可以看到的行/列数 */
  protected spawnRCCount: number = 0;
  /** 多少行/列进行循环 */
  protected spawnItem: number = 0;
  /** 填满之后的总数 */
  protected fullDataNum: number = 0;
  /** 滚动距离 */
  protected rollLong: number = 0;
  /** 容器高度/宽度 */
  protected contentSize: number = 0;
  /** 移动距离 */
  protected moveLong: number = 0;

  protected onLoad() {
    this.contentForm = this.node.getComponent(UITransform);
    this.contentForm.setAnchorPoint(0, 1);
    this.resetListParam();
    if (this.canRoll) {
      this.node.on(NodeEventType.TOUCH_START, this.onTouchStart, this);
      this.node.on(NodeEventType.TOUCH_MOVE, this.onTouchMove, this);
      // this.node.on(NodeEventType.TOUCH_CANCEL, this.onTouchEnd, this);
      // this.node.on(NodeEventType.TOUCH_END, this.onTouchEnd, this);
    }
    if (this.clickEvent.target != null) {
      this.node.on(Node.EventType.TOUCH_END, this.onClick, this);
    }
  }

  /** 初始化参数 */
  resetListParam() {
    const itemRender = this.pool.pop(this.itemName);
    const form = itemRender.getComponent(UITransform);
    this.itemHeight = form.height;
    this.itemWidth = form.width;
    this.moveX = form.width * form.anchorX;
    this.moveY = form.height * form.anchorY - form.height;
    this.pool.push(itemRender);

    if (this.rollDirection == List.ListType.Vertical) {
      this.gridCol = Math.floor((this.contentForm.width + this.spaceX) / (this.itemWidth + this.spaceX));
      if (this.gridCol < 1) this.gridCol = 1;
      this.gridRow = Math.ceil((this.contentForm.height + this.spaceY) / (this.itemHeight + this.spaceY));
      if (this.gridRow < 1) this.gridRow = 1;
      this.spawnRCCount = this.gridRow + 1;
      this.spawnCount = this.gridCol * this.spawnRCCount;
      this.contentForm.width = this.gridCol * (this.itemWidth + this.spaceX) - this.spaceX;
      this.itemSize = this.itemHeight + this.spaceX;
      this.updateFunc = this.itemChangeV;
      this.moveFunc = this.moveItmeV;
      this.grid = this.gridCol;
    } else {
      this.gridRow = Math.floor((this.contentForm.height + this.spaceY) / (this.itemHeight + this.spaceY));
      if (this.gridRow < 1) this.gridRow = 1;
      this.gridCol = Math.ceil((this.contentForm.width + this.spaceX) / (this.itemWidth + this.spaceX));
      if (this.gridCol < 1) this.gridCol = 1;
      this.spawnRCCount = this.gridCol + 1;
      this.spawnCount = this.gridRow * this.spawnRCCount;
      this.contentForm.height = this.gridRow * (this.itemHeight + this.spaceY) - this.spaceY;
      this.itemSize = this.itemWidth + this.spaceY;
      this.updateFunc = this.itemChangeH;
      this.moveFunc = this.moveItmeH;
      this.grid = this.gridRow;
    }
    this.seeNum = this.gridCol * this.gridRow;
  }

  private _tempVec2 = v2(0, 0);
  private _tempVec2_1 = v2(0, 0);
  private onTouchStart(event: EventTouch) {
    event.getUILocation(this._tempVec2);
  }

  // private onTouchEnd(event: EventTouch) {
  //     //
  // }

  private onTouchMove(event: EventTouch) {
    event.getUILocation(this._tempVec2_1);
    if (this.rollDirection == List.ListType.Vertical) {
      this.setRollLong(this._tempVec2_1.y - this._tempVec2.y);
    } else {
      this.setRollLong(this._tempVec2_1.x - this._tempVec2.x);
    }
    this._tempVec2.x = this._tempVec2_1.x;
    this._tempVec2.y = this._tempVec2_1.y;
  }

  /**计算列表的各项参数 */
  protected countListParam() {
    const lineNum = Math.ceil(this.itemDataList.length / this.grid);
    this.contentSize = lineNum * this.itemSize;
    this.fullDataNum = lineNum * this.grid;
    this.spawnItem = MathUtil.lcm(lineNum, this.spawnCount);
  }

  /**
   * 更新改变的item
   * @param begin   开始数
   * @param count     结束数
   */
  protected itemChangeV(begin: number, count: number) {
    const items = this.itemList;
    const nowBegin = Math.floor(this.moveLong / this.itemSize);
    const move = (this.moveLong % this.itemSize) + (this.moveLong < 0 ? this.itemSize : 0);
    begin *= this.gridCol;
    count *= this.gridCol;
    let j = begin > 0 ? begin : (begin % this.spawnItem) + this.spawnItem;
    for (let i = 0; i < count; i++, j++, begin++) {
      const temp = j % this.spawnCount;
      const index = j % this.fullDataNum;
      let item = items[temp];
      const data = this.itemDataList[index];
      if (data == null) {
        if (item != null) item.active = false;
        continue;
      }
      if (item == null) {
        item = this.getItem();
        items[temp] = item;
        this.node.addChild(item);
      }
      const itemRender: ListRender = item.getComponent(ListRender);
      itemRender.itemIndex = index;
      itemRender.data = data;
      itemRender.dataChanged();
      item.setPosition(this.moveX + (j % this.gridCol) * (this.itemWidth + this.spaceX), this.moveY - (Math.floor(begin / this.gridCol) - nowBegin) * (this.itemHeight + this.spaceY) + move);
      item.active = true;
    }
  }

  /**
   * 更新改变的item
   * @param begin     开始数
   * @param count       结束数
   */
  protected itemChangeH(begin: number, count: number) {
    const items = this.itemList;
    const nowBegin = Math.floor(this.moveLong / this.itemSize);
    const move = (this.moveLong % this.itemSize) + (this.moveLong < 0 ? this.itemSize : 0);
    begin *= this.gridCol;
    count *= this.gridCol;
    let j = begin > 0 ? begin : (begin % this.spawnItem) + this.spawnItem;
    for (let i = begin; i < count; i++, j++, begin++) {
      const temp = j % this.spawnCount;
      const index = j % this.fullDataNum;
      let item = items[temp];
      const data = this.itemDataList[index];
      if (data == null) {
        if (item != null) item.active = false;
        continue;
      }
      if (item == null) {
        item = this.getItem();
        items[temp] = item;
        this.node.addChild(item);
      }
      const itemRender: ListRender = item.getComponent(ListRender);
      itemRender.itemIndex = j;
      itemRender.data = data;
      itemRender.dataChanged();
      item.setPosition(this.moveX + (Math.floor(begin / this.gridRow) - nowBegin) * (this.itemWidth + this.spaceX), this.moveY - (j % this.gridRow) * (this.itemHeight + this.spaceY) - move);
      item.active = true;
    }
  }

  protected onUpdateItem(moveCount: number) {
    if (moveCount == 0) return;
    let begin, count;
    if (moveCount > 0) {
      //大于0向上滑
      count = moveCount > this.spawnRCCount ? this.spawnRCCount : moveCount;
      begin = Math.floor(this.moveLong / this.itemSize) + this.spawnRCCount - count;
      this.updateFunc(begin, count);
    } else {
      begin = Math.floor(this.moveLong / this.itemSize);
      count = moveCount < -this.spawnRCCount ? this.spawnRCCount : -moveCount;
    }
    this.updateFunc(begin, count);
  }

  private moveItmeV(long: number) {
    for (let i = 0; i < this.spawnCount; i++) {
      const item = this.itemList[i];
      if (!item || !item.active) continue;
      item.setPosition(item.position.x, item.position.y + long);
    }
  }

  private moveItmeH(long: number) {
    for (let i = 0; i < this.spawnCount; i++) {
      const item = this.itemList[i];
      if (!item || !item.active) continue;
      item.setPosition(item.position.x + long, item.position.y);
    }
  }

  protected onClick(event: EventTouch) {
    const clickPos = event.getUILocation();
    const pos = this.contentForm.convertToNodeSpaceAR(v3(clickPos.x, clickPos.y));
    if (-pos.y % (this.itemHeight + this.spaceY) > this.itemHeight) return;
    if (pos.x % (this.itemWidth + this.spaceX) > this.itemWidth) return;
    const row = Math.floor(-pos.y / (this.itemHeight + this.spaceY));
    const col = Math.floor(pos.x / (this.itemWidth + this.spaceX));
    let i = 0;
    if (this.rollDirection == List.ListType.Vertical) {
      i = this.gridCol * row + col;
    } else {
      i = this.gridRow * col + row;
    }
    i += Math.floor(this.moveLong / this.itemSize);
    i = i % this.fullDataNum;
    if (i < 0) i += this.fullDataNum;
    if (this.itemDataList.length <= i) return;
    // this.clickEvent.emit([this.itemList[i % this.spawnCount], this.clickEvent.customEventData]);
  }

  // eslint-disable-next-line @typescript-eslint/no-unused-vars
  protected update(dt: number): void {
    if (this.rollLong != 0) {
      let temp;
      if (Math.abs(this.rollLong) < this.speed) {
        temp = this.rollLong;
      } else {
        temp = this.rollLong > 0 ? this.speed : -this.speed;
      }
      this.rollLong -= temp;
      this.moveFunc(temp);
      const begin = Math.floor(this.moveLong / this.itemSize);
      this.moveLong += temp;
      const end = Math.floor(this.moveLong / this.itemSize);
      this.onUpdateItem(end - begin);
      temp = this.itemSize * this.spawnItem;
      if (this.moveLong > temp) {
        this.moveLong -= temp;
      } else if (this.moveLong < -temp) {
        this.moveLong += temp;
      }
      if (this.rollLong == 0) {
        this.rollEndBack?.execute();
        this.rollEndBack = null;
      }
    }
  }

  /**获取一个列表项 */
  protected getItem() {
    return this.pool.pop(this.itemName);
  }

  /**
   * 列表数据 (列表数据复制使用，如果列表数据改变，则需要重新设置一遍数据)
   * @param itemDataList item数据列表
   */
  public setData(itemDataList: Array<unknown>) {
    this.itemDataList = itemDataList;
    if (this.node.active) {
      this.countListParam();
      this.moveLong = 0;
      this.updateFunc(0, this.spawnRCCount);
    }
  }

  public setRollTime(time: number, isFixPoisition: boolean = true, back?: CallBackHandler) {
    if (isFixPoisition) {
      const temp = Math.trunc((this.speed * time) / this.itemSize);
      this.rollLong += temp * this.itemSize;
    } else {
      this.rollLong += this.speed * time;
    }
    this.rollEndBack = back;
  }

  private rollEndBack: CallBackHandler;
  public setRollLong(long: number, back?: CallBackHandler) {
    this.rollLong += long;
    this.rollEndBack = back;
  }

  public setRollCount(count: number, back?: CallBackHandler) {
    this.rollLong += count * this.itemSize;
    this.rollEndBack = back;
  }

  public sliceItem<T extends ListRender>(begin: number = 0, end?: number): T[] {
    const nowBegin = Math.floor(this.moveLong / this.itemSize);
    const index = nowBegin > 0 ? nowBegin % this.spawnCount : (nowBegin % this.spawnCount) + this.spawnCount;
    if (end == undefined) {
      end = this.seeNum;
    }
    if (begin > end) throw new Error('ListRoll=>sliceItem(): begin > end');
    end = end - begin;
    const result = [];
    for (let i = 0; i < end; i++) {
      const element = this.itemList[(index + i) % this.spawnCount].getComponent(ListRender);
      result.push(element);
    }
    return result;
  }
}
